<?php
/**
 * @copyright 2008 cryonyx
 */

/**
 * VC_Core class contains shell-independent functions for processing raw statistics.
 */
class VC_Core {
	private	$views;
	private	$visitors;
	private	$latest_view;
	private	$db;

	/**
	 * Construction method. Initializes necessary values.
	 *
	 * @param VC_DB $dbo Database object handle
	 */
	public function __construct(&$dbo) {
		$this->db	=	$dbo;
	}

	/**
	 * Retreives raw statistics from database and puts it to local storage
	 *
	 * @param integer $id Identifier of post/entry/etc.
	 */
	private function get_raw_stats($id) {
		//	Fetching data from database
		$tmp				=	$this->db->fetch_stat($id);
		
		//	Storing the number of views for a requested entry 
		$this->views		=	$tmp['views'];
		//	Forming array of encoded IPs and putting it in a local storage
		$this->visitors		=	str_split($tmp['visitors'], 4);
		//	Storing the date of the latest view of a requested entry 
		$this->latest_view	=	$tmp['latest_view'];
		
		//	Collecting the garbage
		unset($tmp);
	}
	
	/**
	 * Increases counters for requested entry
	 *
	 * @param integer $id
	 * @return boolean
	 */
	private function count_up($id) {
		//	Checking the input for validity
		if(	!isset($id)		||
			!is_numeric($id)
		) {
			return FALSE;
		}
		
		//	Loading data from DB and storing them in local variables
		self::get_raw_stats($id);	

		//	Increasing counters in local storage
		$this->views++;												//	Increasing the counter of views by 1
		$curr_ip	=	self::encode_ips($_SERVER['REMOTE_ADDR']);	//	Compressing the IP of current visitor
		if(!in_array($curr_ip, $this->visitors)) {					//	Checking for unique IP
			$this->visitors[]	=	$curr_ip;						//	In case the IP is unique, storing it in the local storage
		}

		//	Forming an array for storing in DB
		$tmp	=	array(
			'views'			=>	$this->views,
			'visitors'		=>	implode($this->visitors),	//	Implode all unique compressed IPs into one string for storing in DB
			'latest_view'	=>	$this->latest_view
		);
		
		//	Saving data from array to DB
		$this->db->store_stat($id, $tmp);
		
		//	Clearing memory
		unset($tmp);
		
		//	Returning the marker of success
		return TRUE;
	}

	/**
	 * Generates the output string ready for displaying on a web-page.
	 *
	 * @param integer $id
	 * @param string $pattern
	 * @return string
	 */
	public function show_it_to_me($id) {
		//	Increasing counters
		self::count_up($id);
		//	Getting pattern for string from options
		$pattern		=	$_SESSION['vc']['pattern'];
		//	Initializing the array of substitutes
		$substitutes	=	array(
			'%t'	=>	$this->views,
			'%v'	=>	count($this->visitors),
			'%d'	=>	$this->latest_view,
			'%i'	=>	'<a href="http://blog.cerebrate.ru/vc/" title="This site proudly uses VC 1.0"><img src="vc.gif" alt="Views Counter v. 1.0" /></a>',
			'%%'	=>	'%'
		);
		
		//	Returning formatted string
		return	strtr($pattern, $substitutes);
	}
	
	/**
	 * Compresses a series of IPs to compact view.
	 *
	 * @param string|array $arr
	 * @return string
	 */
	private static function encode_ips($arr) {
		//	In case we have only one string value as an input parameter, transform it to an array
		if(!is_array($arr)) {$arr = array($arr);};
		
		//	Compress each element of the input array and append it to the result string.
		foreach($arr as $ip) {
			//	Compression method is ment to be simple and fast, but to show good compression.
			//	The method used below shows the compression rate in a range [27%; 57%]
			$the_thing	.=	implode(	//	Glue result characters alltogether and append to result string
				array_map('chr',		//	Transform each of 4 numbers into an ASCII character
					explode('.', $ip)	//	Separate the IP into 4 numeric blocks
				)
			);
		}
		
		//	Return the result string
		return $the_thing;
	}
	
	/**
	 * Decodes IPs compressed using encode_ips method
	 *
	 * @param string $str The string containing encoded IPs
	 * @return array The array of result IPs
	 */
	private static function decode_ips($str) {
		//	Split the input string into 4-character-chunks, each representing definite IP
		$the_thing	=	str_split($str, 4);
		//	Count the number of chunks
		$max		=	count($the_thing);
		//	Prepare temporary array
		$tmp		=	array();
		//	For each chunk...
		for($i = 0; $i < $max; $i++) {
			//	For each character in a chunk... 
			for($j = 0; $j < 4; $j++) {
				//	In case the chunk is corrupted and contains not all the elements...
				if(!isset($the_thing[$i]{$j})) {
					//	Destroy the chunk
					unset($the_thing[$i]);
					//	Clear the temporary array
					$tmp	=	array();
					//	Proceed to the next chunk (usually only the last chunk is corrupted, thus
					//	this will stop processing chunks and jump to returning  of result array). 
					continue 2;
				}
				
				//	Add to temporary array decoded number
				array_push($tmp, ord($the_thing[$i]{$j}));
			}
			
			//	Transform encoded chunk into regular IP
			$the_thing[$i]	=	implode('.', $tmp);
			//	Clear the temporary array
			$tmp			=	array();
		}
		
		//	Wipe the memory
		unset($tmp);
		
		//	Remove empty chunks
		$the_thing	=	array_diff($the_thing, array(NULL));
		
		//	Return result array
		return	$the_thing;
	}

}
?>